#include "head.h"
#define BUFFER_SIZE 4096

typedef struct file_train_s {
    ssize_t file_size;
    char name[25];
    unsigned char hash[MD5_DIGEST_LENGTH];
}file_train_t;

// 隐藏光标
static void hide_cursor() {
    printf("\e[?25l");
    fflush(stdout);
}

// 显示光标
static void show_cursor() {
    printf("\e[?25h");
    fflush(stdout);
}

static void show_progress_bar(int progress, int total) {
    int bar_width = 50;  // 进度条宽度
    float percentage = (float)progress / total;
    int pos = bar_width * percentage;

    printf("[");
    for (int i = 0; i < bar_width; ++i) {
        if (i < pos) printf("=");
        else if (i == pos) printf(">");
        else printf(" ");
    }
    printf("] %d%%\r", (int)(percentage * 100));
    fflush(stdout);
}

//
static void calculate_file_md5(int file_fd, unsigned char output[MD5_DIGEST_LENGTH]) {
    MD5_CTX md5Context;
    unsigned char buffer[4096];
    size_t bytesRead;

    // 初始化 MD5 上下文
    MD5_Init(&md5Context);

    // 读取文件并更新 MD5 哈希值

    while ((bytesRead = read(file_fd, buffer, sizeof(buffer))) != 0) {
        MD5_Update(&md5Context, buffer, bytesRead);
    }

    // 计算最终的 MD5 哈希值
    MD5_Final(output, &md5Context);

}


//用于实现客户端的ls操作
//int client_ls(int socket_fd)
//{
//    printf("1\n");
//    char buf[1024]="test";
//    send(socket_fd,buf,sizeof(buf),MSG_NOSIGNAL);
//    return 0;
//}

//用于实现客户端的cd操作
//int client_cd(int socket_fd,char*cur_path,char*arg)
//{
//    return 0;
//}
//用于实现客户端的pwd操作
//int client_pwd(char*cur_path)
//{
//    return 0;
//}
//用于实现客户端的mkdir操作
//int client_cdir(int socket_fd,char*cur_path)
//    //用于实现客户端上传文件的操作
//{
//    return 0;
//}

int client_upload(int socket_fd,char*cur_path)
    //用于实现客户端下载文件的操作
{

    const char *file_name = cur_path;
    int file_fd = open(file_name, O_RDONLY);
    printf("file_name is:%s\n",file_name);
    //获取本地文件大小
    struct stat stat_file;
    bzero(&stat_file, sizeof(stat_file));
    fstat(file_fd, &stat_file);
    ssize_t file_size = stat_file.st_size;

    printf("file_size = %ld\n", file_size);


    //获得文件的MD5值
    unsigned char hash_md5[MD5_DIGEST_LENGTH];
    calculate_file_md5(file_fd, hash_md5);
    printf("MD5 hash of %s: ", file_name);
   // for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {


   //     printf("%02x", hash_md5[i]);
   // }
    puts("\n");

    //将获取的信息放入到传输协议结构体中
    file_train_t file_info;
    bzero(&file_info, sizeof(file_info));
    file_info.file_size = file_size;
    strcpy(file_info.name, file_name);
    memcpy(file_info.hash, hash_md5, sizeof(hash_md5));

    //向服务端发送文件名、文件大小、以及文件的哈希值
    int res_send = send(socket_fd, &file_info, sizeof(file_train_t), MSG_NOSIGNAL);
    ERROR_CHECK(res_send, -1, "send");
    printf("file_info发送完成\n");

    //等待服务端对比文件大小回复偏移量: 0表示文件未接收过
    off_t offset;
    int res_recv = recv(socket_fd, &offset, sizeof(off_t), MSG_WAITALL);
    ERROR_CHECK(res_recv, -1, "recv");
    printf("offset = %ld\n", offset);

    char *file_map = (char *)mmap(NULL, file_size, PROT_READ, MAP_SHARED, file_fd, 0);
    ERROR_CHECK(file_map, NULL, "mmap");


    //根据偏移量计算出发送数据的位置
    //隐藏光标
    hide_cursor();
    while (offset < file_size) {
        //计算一次发送的数据大小，< = 一个页面大小 4096
        size_t bytes_to_send = (file_size - offset > BUFFER_SIZE) ? BUFFER_SIZE : file_size - offset;

        ssize_t bytes_sent = send(socket_fd, file_map + offset, bytes_to_send, MSG_NOSIGNAL);
        ERROR_CHECK(bytes_sent, -1, "send");

        offset += bytes_sent;
        show_progress_bar(offset, file_size);
    }

    //发送文件完成，恢复光标
    show_cursor(); 

    puts("");

    munmap(file_map, file_size);
    close(file_fd);

    return 0;
}

int client_download(int socket_fd,char*cur_path)
{
    //先接收服务端发送的文件信息
    file_train_t file_info;
    bzero(&file_info, sizeof(file_info));
    int res_recv = recv(socket_fd, &file_info, sizeof(file_info), MSG_WAITALL);
    ERROR_CHECK(res_recv, -1, "recv");
    if (res_recv == 0) {
        printf("server is disconnected\n");
        return -1;
    }

    char *file_name = file_info.name;
    ssize_t file_size = file_info.file_size;
    unsigned char *hash_md5 = file_info.hash;
   // for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
   //     printf("%02x", hash_md5[i]);
   // }
    puts("");

    //通过文件名打开本地文件，并获得偏移量
    int file_fd = open(file_name, O_WRONLY | O_CREAT, 0644);
    struct stat stat_file;
    bzero(&stat_file, sizeof(stat_file));
    fstat(file_fd, &stat_file);

    off_t offset = stat_file.st_size;

    //给服务端发送文件的偏移量
    int res_send = send(socket_fd, &offset, sizeof(off_t), MSG_NOSIGNAL);
    ERROR_CHECK(res_send, -1, "send");

    //移动文件写入位置到偏移量处
    lseek(file_fd, offset, SEEK_SET); 
    char buf[BUFFER_SIZE] = { 0 }; 
    ssize_t bytes_received;
    ssize_t bytes_to_send;

    hide_cursor();
    while (offset < file_size) {
        bytes_to_send = (file_size - offset > BUFFER_SIZE) ? BUFFER_SIZE : file_size - offset;
        bytes_received = recv(socket_fd, buf, bytes_to_send, MSG_WAITALL);
        ERROR_CHECK(bytes_received, -1, "recv");
        if (bytes_received == 0) {
            printf("server is disconnected\n");
            close(file_fd);
            return -1;
        }

        write(file_fd, buf, bytes_received);

        offset += bytes_received;
        show_progress_bar(offset, file_size);
    }
    show_cursor();

    puts("");


    close(file_fd);
    return 0;
}
//int client_rm(int socket_fd,char*arg)
//
//{
//    return 0;
//}
